{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Using Functional API to build CNN\n",
    "~99.3% test accuracy"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/usr/local/lib/python3.6/site-packages/h5py/__init__.py:36: FutureWarning: Conversion of the second argument of issubdtype from `float` to `np.floating` is deprecated. In future, it will be treated as `np.float64 == np.dtype(float).type`.\n",
      "  from ._conv import register_converters as _register_converters\n",
      "Using TensorFlow backend.\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "_________________________________________________________________\n",
      "Layer (type)                 Output Shape              Param #   \n",
      "=================================================================\n",
      "input_1 (InputLayer)         (None, 28, 28, 1)         0         \n",
      "_________________________________________________________________\n",
      "conv2d_1 (Conv2D)            (None, 26, 26, 64)        640       \n",
      "_________________________________________________________________\n",
      "max_pooling2d_1 (MaxPooling2 (None, 13, 13, 64)        0         \n",
      "_________________________________________________________________\n",
      "conv2d_2 (Conv2D)            (None, 11, 11, 64)        36928     \n",
      "_________________________________________________________________\n",
      "max_pooling2d_2 (MaxPooling2 (None, 5, 5, 64)          0         \n",
      "_________________________________________________________________\n",
      "conv2d_3 (Conv2D)            (None, 3, 3, 64)          36928     \n",
      "_________________________________________________________________\n",
      "flatten_1 (Flatten)          (None, 576)               0         \n",
      "_________________________________________________________________\n",
      "dropout_1 (Dropout)          (None, 576)               0         \n",
      "_________________________________________________________________\n",
      "dense_1 (Dense)              (None, 10)                5770      \n",
      "=================================================================\n",
      "Total params: 80,266\n",
      "Trainable params: 80,266\n",
      "Non-trainable params: 0\n",
      "_________________________________________________________________\n",
      "Train on 60000 samples, validate on 10000 samples\n",
      "Epoch 1/20\n",
      "60000/60000 [==============================] - 54s 904us/step - loss: 0.2666 - acc: 0.9176 - val_loss: 0.0542 - val_acc: 0.9835\n",
      "Epoch 2/20\n",
      "60000/60000 [==============================] - 55s 913us/step - loss: 0.0719 - acc: 0.9775 - val_loss: 0.0380 - val_acc: 0.9886\n",
      "Epoch 3/20\n",
      "60000/60000 [==============================] - 55s 914us/step - loss: 0.0505 - acc: 0.9840 - val_loss: 0.0384 - val_acc: 0.9863\n",
      "Epoch 4/20\n",
      "60000/60000 [==============================] - 56s 937us/step - loss: 0.0408 - acc: 0.9871 - val_loss: 0.0286 - val_acc: 0.9900\n",
      "Epoch 5/20\n",
      "60000/60000 [==============================] - 56s 933us/step - loss: 0.0358 - acc: 0.9886 - val_loss: 0.0253 - val_acc: 0.9914\n",
      "Epoch 6/20\n",
      "60000/60000 [==============================] - 56s 927us/step - loss: 0.0297 - acc: 0.9906 - val_loss: 0.0238 - val_acc: 0.9921\n",
      "Epoch 7/20\n",
      "60000/60000 [==============================] - 56s 932us/step - loss: 0.0275 - acc: 0.9913 - val_loss: 0.0310 - val_acc: 0.9897\n",
      "Epoch 8/20\n",
      "60000/60000 [==============================] - 54s 902us/step - loss: 0.0238 - acc: 0.9924 - val_loss: 0.0237 - val_acc: 0.9918\n",
      "Epoch 9/20\n",
      "60000/60000 [==============================] - 57s 952us/step - loss: 0.0220 - acc: 0.9925 - val_loss: 0.0234 - val_acc: 0.9923\n",
      "Epoch 10/20\n",
      "60000/60000 [==============================] - 84s 1ms/step - loss: 0.0194 - acc: 0.9937 - val_loss: 0.0286 - val_acc: 0.9915\n",
      "Epoch 11/20\n",
      "60000/60000 [==============================] - 100s 2ms/step - loss: 0.0176 - acc: 0.9939 - val_loss: 0.0203 - val_acc: 0.9931\n",
      "Epoch 12/20\n",
      "60000/60000 [==============================] - 130s 2ms/step - loss: 0.0160 - acc: 0.9948 - val_loss: 0.0255 - val_acc: 0.9921\n",
      "Epoch 13/20\n",
      "60000/60000 [==============================] - 129s 2ms/step - loss: 0.0146 - acc: 0.9953 - val_loss: 0.0259 - val_acc: 0.9921\n",
      "Epoch 14/20\n",
      "60000/60000 [==============================] - 130s 2ms/step - loss: 0.0149 - acc: 0.9950 - val_loss: 0.0240 - val_acc: 0.9926\n",
      "Epoch 15/20\n",
      "60000/60000 [==============================] - 134s 2ms/step - loss: 0.0119 - acc: 0.9960 - val_loss: 0.0218 - val_acc: 0.9937\n",
      "Epoch 16/20\n",
      "60000/60000 [==============================] - 136s 2ms/step - loss: 0.0122 - acc: 0.9958 - val_loss: 0.0221 - val_acc: 0.9932\n",
      "Epoch 17/20\n",
      "60000/60000 [==============================] - 127s 2ms/step - loss: 0.0123 - acc: 0.9962 - val_loss: 0.0236 - val_acc: 0.9934\n",
      "Epoch 18/20\n",
      "60000/60000 [==============================] - 135s 2ms/step - loss: 0.0095 - acc: 0.9968 - val_loss: 0.0267 - val_acc: 0.9922\n",
      "Epoch 19/20\n",
      "60000/60000 [==============================] - 145s 2ms/step - loss: 0.0100 - acc: 0.9967 - val_loss: 0.0218 - val_acc: 0.9930\n",
      "Epoch 20/20\n",
      "60000/60000 [==============================] - 134s 2ms/step - loss: 0.0087 - acc: 0.9970 - val_loss: 0.0228 - val_acc: 0.9929\n",
      "10000/10000 [==============================] - 5s 543us/step\n",
      "\n",
      "Test accuracy: 99.3%\n"
     ]
    }
   ],
   "source": [
    "from __future__ import absolute_import\n",
    "from __future__ import division\n",
    "from __future__ import print_function\n",
    "\n",
    "import numpy as np\n",
    "from keras.layers import Dense, Dropout, Input\n",
    "from keras.layers import Conv2D, MaxPooling2D, Flatten\n",
    "from keras.models import Model\n",
    "from keras.datasets import mnist\n",
    "from keras.utils import to_categorical\n",
    "\n",
    "\n",
    "# load MNIST dataset\n",
    "(x_train, y_train), (x_test, y_test) = mnist.load_data()\n",
    "\n",
    "# from sparse label to categorical\n",
    "num_labels = len(np.unique(y_train))\n",
    "y_train = to_categorical(y_train)\n",
    "y_test = to_categorical(y_test)\n",
    "\n",
    "# reshape and normalize input images\n",
    "image_size = x_train.shape[1]\n",
    "x_train = np.reshape(x_train,[-1, image_size, image_size, 1])\n",
    "x_test = np.reshape(x_test,[-1, image_size, image_size, 1])\n",
    "x_train = x_train.astype('float32') / 255\n",
    "x_test = x_test.astype('float32') / 255\n",
    "\n",
    "# network parameters\n",
    "input_shape = (image_size, image_size, 1)\n",
    "batch_size = 128\n",
    "kernel_size = 3\n",
    "filters = 64\n",
    "dropout = 0.3\n",
    "\n",
    "# use functional API to build cnn layers\n",
    "inputs = Input(shape=input_shape)\n",
    "y = Conv2D(filters=filters,\n",
    "           kernel_size=kernel_size,\n",
    "           activation='relu')(inputs)\n",
    "y = MaxPooling2D()(y)\n",
    "y = Conv2D(filters=filters,\n",
    "           kernel_size=kernel_size,\n",
    "           activation='relu')(y)\n",
    "y = MaxPooling2D()(y)\n",
    "y = Conv2D(filters=filters,\n",
    "           kernel_size=kernel_size,\n",
    "           activation='relu')(y)\n",
    "# image to vector before connecting to dense layer\n",
    "y = Flatten()(y)\n",
    "# dropout regularization\n",
    "y = Dropout(dropout)(y)\n",
    "outputs = Dense(num_labels, activation='softmax')(y)\n",
    "\n",
    "# build the model by supplying inputs/outputs\n",
    "model = Model(inputs=inputs, outputs=outputs)\n",
    "# network model in text\n",
    "model.summary()\n",
    "\n",
    "# classifier loss, Adam optimizer, classifier accuracy\n",
    "model.compile(loss='categorical_crossentropy',\n",
    "              optimizer='adam',\n",
    "              metrics=['accuracy'])\n",
    "\n",
    "# train the model with input images and labels\n",
    "model.fit(x_train,\n",
    "          y_train,\n",
    "          validation_data=(x_test, y_test),\n",
    "          epochs=20,\n",
    "          batch_size=batch_size)\n",
    "\n",
    "# model accuracy on test dataset\n",
    "score = model.evaluate(x_test, y_test, batch_size=batch_size)\n",
    "print(\"\\nTest accuracy: %.1f%%\" % (100.0 * score[1]))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
